package com.hj.springframework3.aop;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;

/**
 * @Author : hujing
 * @Date: 2025/7/8 14:34
 * @Description: 动态创建代理对象
 */
public class ProxyResolver {
    final Logger logger = LoggerFactory.getLogger(getClass());

    final ByteBuddy byteBuddy = new ByteBuddy();
    
    private static ProxyResolver INSTANCE;
    
    public static ProxyResolver getInstance(){
        if (INSTANCE == null){
            INSTANCE = new ProxyResolver();
        }
        return INSTANCE;
    }

    /**
     * 动态创建代理对象
     * @param bean 目标对象
     * @param handler 代理对象在调用方法时的行为
     * @return
     * @param <T>
     */
    public <T> T createProxy(T bean, InvocationHandler handler) {
        Class<?> targetClass = bean.getClass();
        
        // 创建代理类
        Class<?> proxyClass = this.byteBuddy
                // 创建一个继承自 targetClass 的子类，并使用默认构造函数
                .subclass(targetClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
                // 拦截所有public方法
                .method(ElementMatchers.isPublic())
                //为匹配到的方法设置拦截器（拦截器是一个 InvocationHandler 对象），当这些方法被调用时，会执行 InvocationHandlerAdapter 中定义的逻辑
                //handler.invoke(bean, method, args): 这里定义了拦截器的具体行为，即在代理对象的方法被调用时，会先调用 InvocationHandler 的 invoke 方法，传入目标对象 bean、被调用的方法 method 和参数 args
                .intercept(InvocationHandlerAdapter.of(
                        (proxy, method, args) -> handler.invoke(bean, method, args)
                ))
                //生成动态类的字节码
                .make()
                //将生成的字节码加载到目标类的类加载器中
                .load(targetClass.getClassLoader())
                //获取已经加载的代理类
                .getLoaded();
        
        //创建代理类的实例
        Object proxy;
        try {
            proxy = proxyClass.getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        
        // 返回代理对象
        return (T) proxy;
    }
}
